﻿#region Copyright(C) 2010-2010 NCS Pte. Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2010 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME	    : iConnect .Net Application Framework
// COMPONENT ID	    : CBEC.Core.Wcf
// COMPONENT DESC   : Core components for WCF.	
//
// CREATED DATE/BY  : 18 Aug 2010 / Gao Ye
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace CBEC.Core.ApplicationContexts.Wcf
{
    /// <summary>
    /// A behavior to initialize transaction id based on applied <see cref="OperationBehaviorAttribute"/>.
    /// </summary>
    public class TransactionIdInitializeBehaviorAttribute : Attribute, IServiceBehavior,IEndpointBehavior, IOperationBehavior
    {
        #region IServiceBehavior
        /// <summary>
        /// Provides the ability to pass custom data to binding elements to support the contract implementation.
        /// </summary>
        /// <param name="serviceDescription">The service description of the service.</param>
        /// <param name="serviceHostBase">The host of the service.</param>
        /// <param name="endpoints">The service endpoints.</param>
        /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        /// <summary>
        /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The host that is currently being built.</param>
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            List<Tuple<string, string, string>> identifiers = new List<Tuple<string, string, string>>();

            foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
            {
                foreach (OperationDescription operation in endpoint.Contract.Operations)
                {
                    OperationBehaviorAttribute operationBehaviorAttribute = operation.Behaviors.Find<OperationBehaviorAttribute>();
                    if (null != operationBehaviorAttribute && operationBehaviorAttribute.TransactionScopeRequired)
                    {
                        identifiers.Add(Tuple.Create<string, string, string>(operation.Name, endpoint.Contract.Name, endpoint.Contract.Namespace));
                    }
                }
            }

            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
                    {
                        if (identifiers.Any(identifier => identifier.Item1 == operation.Name && identifier.Item2 == endpointDispatcher.ContractName && identifier.Item3 == endpointDispatcher.ContractNamespace))
                        {
                            operation.CallContextInitializers.Add(new TransactionIdInitializer());
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion

        #region IEndpointBehavior

        /// <summary>
        /// Implement to pass data at runtime to bindings to support custom behavior.
        /// </summary>
        /// <param name="endpoint">The endpoint to modify.</param>
        /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        /// <summary>
        /// Implements a modification or extension of the client across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that is to be customized.</param>
        /// <param name="clientRuntime">The client runtime to be customized.</param>
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        /// <summary>
        /// Implements a modification or extension of the service across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that exposes the contract.</param>
        /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            List<Tuple<string, string, string>> identifiers = new List<Tuple<string, string, string>>();

            foreach (OperationDescription operation in endpoint.Contract.Operations)
            {
                OperationBehaviorAttribute operationBehaviorAttribute = operation.Behaviors.Find<OperationBehaviorAttribute>();
                if (null != operationBehaviorAttribute && operationBehaviorAttribute.TransactionScopeRequired)
                {
                    identifiers.Add(Tuple.Create<string, string, string>(operation.Name, endpoint.Contract.Name, endpoint.Contract.Namespace));
                }
            }

            foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
            {
                if (identifiers.Any(identifier => identifier.Item1 == operation.Name && identifier.Item2 == endpointDispatcher.ContractName && identifier.Item3 == endpointDispatcher.ContractNamespace))
                {
                    operation.CallContextInitializers.Add(new TransactionIdInitializer());
                }
            }
        }

        /// <summary>
        /// Implement to confirm that the endpoint meets some intended criteria.
        /// </summary>
        /// <param name="endpoint">The endpoint to validate.</param>
        public void Validate(ServiceEndpoint endpoint)
        {
        }
        #endregion

        #region IOperationBehavior
        /// <summary>
        /// Implement to pass data at runtime to bindings to support custom behavior.
        /// </summary>
        /// <param name="operationDescription">The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.</param>
        /// <param name="bindingParameters">The collection of objects that binding elements require to support the behavior.</param>
        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        /// <summary>
        /// Implements a modification or extension of the client across an operation.
        /// </summary>
        /// <param name="operationDescription">The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.</param>
        /// <param name="clientOperation">The run-time object that exposes customization properties for the operation described by <paramref name="operationDescription"/>.</param>
        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        /// <summary>
        /// Implements a modification or extension of the service across an operation.
        /// </summary>
        /// <param name="operationDescription">The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.</param>
        /// <param name="dispatchOperation">The run-time object that exposes customization properties for the operation described by <paramref name="operationDescription"/>.</param>
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            OperationBehaviorAttribute operationBehaviorAttribute = operationDescription.Behaviors.Find<OperationBehaviorAttribute>();
            if (null != operationBehaviorAttribute && operationBehaviorAttribute.TransactionScopeRequired)
            {
                dispatchOperation.CallContextInitializers.Add(new TransactionIdInitializer());
            }
        }

        /// <summary>
        /// Implement to confirm that the operation meets some intended criteria.
        /// </summary>
        /// <param name="operationDescription">The operation being examined. Use for examination only. If the operation description is modified, the results are undefined.</param>
        public void Validate(OperationDescription operationDescription)
        {
        }
        #endregion
    }
}
